package core

import (
	"errors"
	"fmt"
	"gitee.com/hongzhaomin/gspring/annotation"
	"gitee.com/hongzhaomin/gspring/consts"
	"gitee.com/hongzhaomin/gspring/enums"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/gspring/tools"
	"reflect"
)

var _ iface.BeanDefinitionRegistryPostProcessor = (*ConfigurationPostProcessor)(nil)

type ConfigurationPostProcessor struct {
	resolvedRefTypeSet map[reflect.Type]struct{}
}

func (my *ConfigurationPostProcessor) RepeatableCall() bool {
	return true
}

func (my *ConfigurationPostProcessor) PostProcessBeanFactory(iface.ListableBeanFactory) {
}

func (my *ConfigurationPostProcessor) PostProcessorBeanDefinitionRegistry(registry iface.BeanDefinitionRegistry) {
	if my.resolvedRefTypeSet == nil {
		my.resolvedRefTypeSet = make(map[reflect.Type]struct{}, 16)
	}

	my.doPostProcessorBeanDefinitionRegistry(registry)
}

func (my *ConfigurationPostProcessor) doPostProcessorBeanDefinitionRegistry(registry iface.BeanDefinitionRegistry) {
	for _, parentBeanName := range registry.GetBeanDefinitionNames() {
		err, parentBD := registry.GetBeanDefinition(parentBeanName)
		if err != nil {
			panic(err)
		}

		refType := parentBD.GetObjRefType()
		if _, ok := my.resolvedRefTypeSet[refType]; ok {
			// 解析过了则跳过
			continue
		}

		// 实现了 annotation.configurationAnno 接口的结构体才能被扫描
		if !enums.GetBeanKindEnumByType(refType).Is(enums.Configuration) {
			continue
		}

		my.resolvedRefTypeSet[refType] = struct{}{}

		methodInfos := my.checkMethodsAndCollectMethodInfos(refType)
		methodSize := len(methodInfos)
		if methodSize == 0 {
			continue
		}

		for _, method := range methodInfos {
			beanName := tools.GetBeanName(method.MethodName)
			if beanFactory, ok := registry.(iface.ListableBeanFactory); ok {
				if !beanFactory.ContainsLocalBean(beanName) {
					bd := NewConfigurableBeanDefinition(beanName, parentBD, method)
					registry.RegisterBeanDefinition(beanName, bd)
				}
			} else {
				if !registry.ContainsBeanDefinition(beanName) {
					bd := NewConfigurableBeanDefinition(beanName, parentBD, method)
					registry.RegisterBeanDefinition(beanName, bd)
				}
			}
		}
	}
}

func (my *ConfigurationPostProcessor) checkMethodsAndCollectMethodInfos(refType reflect.Type) []*iface.CreateBeanMethodInfo {
	beanFullPathName := tools.GetFullPathNameByRt(refType)

	// @Bean标注的方法需要满足条件：
	// 		1、bean名称：方法名的首字母小写
	// 		2、有且只有一个返回值，指针或者接口类型，表示指向bean对象的指针或符合接口类型的指针
	//methodNames, ok := tools.FindAnnotationValueByType(refType, (*annotation.Configuration)(nil), consts.Bean)
	//if !ok {
	//	return nil
	//}
	//methodNameList := tools.SpitNotLetterAndNumber(methodNames)

	// 2025.2.9号改为重新定义属性，标注tag[@Bean]作为方法名称
	refType = tools.IndirectReflectType(refType)
	methodNameList := make([]string, 0, refType.NumField())
	for i := 0; i < refType.NumField(); i++ {
		structField := refType.Field(i)
		if structField.Anonymous {
			continue
		}
		// 2025.3.20号：新增一种解析方式，使用 [annotation.Bean] 作为字段的类型，字段首字母大写即为方法名称
		rtField := tools.IndirectReflectType(structField.Type)
		if rtField == reflect.TypeOf((*annotation.Bean)(nil)).Elem() {
			methodName := tools.FirstLetter2Upper(structField.Name)
			methodNameList = append(methodNameList, methodName)
		} else {
			if methodName, ok := structField.Tag.Lookup(consts.Bean); ok {
				methodNameList = append(methodNameList, methodName)
			}
		}
	}
	if len(methodNameList) <= 0 {
		return nil
	}

	configMethodInfoList := make([]*iface.CreateBeanMethodInfo, 0, len(methodNameList))
	for _, methodName := range methodNameList {
		var ReceiverNotPtr = true
		// 校验方法是否存在，并是否可导出
		method, ok := refType.MethodByName(methodName)
		if !ok {
			method, ok = reflect.PointerTo(refType).MethodByName(methodName)
			if !ok {
				panic(errors.New(fmt.Sprintf("%s method [%s] is not exist", beanFullPathName, methodName)))
			}
			ReceiverNotPtr = false
		}

		if !method.IsExported() {
			panic(errors.New(fmt.Sprintf("%s method [%s] is not exported", beanFullPathName, methodName)))
		}

		// 注意：这里的方法第一个入参是receiver
		rvMethod := method.Func
		rtMethod := rvMethod.Type()

		// 入参校验并收集，因为方法第一个入参是receiver，所以方法参数从第二个开始校验解析
		numIn := rtMethod.NumIn()
		var rtParamIns []reflect.Type
		if numIn > 1 {
			rtParamIns = make([]reflect.Type, 0, numIn-1)
			for i := 1; i < numIn; i++ {
				rtIn := rtMethod.In(i)
				if !((rtIn.Kind() == reflect.Ptr && rtIn.Elem().Kind() == reflect.Struct) ||
					rtIn.Kind() == reflect.Interface) {
					msg := fmt.Sprintf("%s method [%s] in param's type must be (ptr to struct) or interface",
						beanFullPathName, methodName)
					panic(errors.New(msg))
				}
				rtParamIns = append(rtParamIns, rtIn)
			}
		}

		// 返回值校验并收集
		numOut := rtMethod.NumOut()
		if numOut != 1 {
			panic(errors.New(fmt.Sprintf("%s method [%s] must be has only one return value",
				beanFullPathName, methodName)))
		}
		rtDeclaredReturn := rtMethod.Out(0)
		if !((rtDeclaredReturn.Kind() == reflect.Ptr && rtDeclaredReturn.Elem().Kind() == reflect.Struct) ||
			rtDeclaredReturn.Kind() == reflect.Interface) {
			msg := fmt.Sprintf("%s method [%s] returned value's type must be (ptr to struct) or interface",
				beanFullPathName, methodName)
			panic(errors.New(msg))
		}

		configMethodInfoList = append(configMethodInfoList,
			&iface.CreateBeanMethodInfo{
				MethodName:           methodName,
				RtMethod:             rtMethod,
				RvMethod:             rvMethod,
				ReceiverNotPtr:       ReceiverNotPtr,
				RtDeclaredParamIns:   rtParamIns,
				RtDeclaredReturnBean: rtDeclaredReturn,
			})
	}
	return configMethodInfoList
}
